/**
 * 
 */
package com.google.code.netz.wfe;

import com.google.code.netz.wfe.api.ILifecycleHandler;
import com.google.code.netz.wfe.api.WorkflowException;
import com.google.code.netz.wfe.events.CaseEvent;
import com.google.code.netz.wfe.events.EventManager;
import com.google.code.netz.wfe.events.PlaceEvent;
import com.google.code.netz.wfe.events.CaseEvent.CaseEventType;
import com.google.code.netz.wfe.events.PlaceEvent.PlaceEventType;
import com.google.code.netz.wfe.model.ICase;
import com.google.code.netz.wfe.model.IToken;
import com.google.code.netz.wfe.model.Place;
import com.google.code.netz.wfe.model.State;

/**
 * @author <a href='mailto:rahul.thakur.xdev@gmail.com'>Rahul Thakur</a>
 * @since 1.0
 * @version $Id$
 */
public class CaseLifecycleHandler implements ILifecycleHandler<ICase<?>> {

	/**
	 * Mutex to lock state updates to the Case.
	 */
	private static Object mutex = new Object();

	/**
	 * {@inheritDoc}
	 * 
	 * @see com.google.code.netz.wfe.api.ILifecycleHandler#resume(com.google.code.netz.wfe.api.IManaged,
	 *      java.lang.Object)
	 */
	@Override
	public void resume(ICase<?> entity, Object workflowContext) throws WorkflowException {
		// TODO: Case status should be updated in a synchronized block
		// Resume Case only if it is suspended
		synchronized (mutex) {
			if (entity.getCurrentState() == State.STATE_SUSPENDED) {
				entity.setCurrentState(State.STATE_RUNNING);
				EventManager.getInstance().dispatchEvent(new CaseEvent<ICase>(entity, CaseEventType.CASE_OPENED));
			} else
				throw new WorkflowException(String.format(
						"Case %s could not be resumed, it is not in a suspended state.", entity.getLabel()));
		}
	}

	/**
	 * {@inheritDoc}
	 * <p>
	 * Opens the specified Case.
	 * 
	 * @see com.google.code.netz.wfe.api.ILifecycleHandler#start(com.google.code.netz.wfe.api.IManaged,
	 *      java.lang.Object)
	 */
	@Override
	public void start(ICase<?> entity, Object workflowContext) throws WorkflowException {
		synchronized (mutex) {
			// TODO: Case status should be updated in a synchronized block
			if (entity.getCurrentState() == State.STATE_ENABLED) {
				entity.setCurrentState(State.STATE_RUNNING);
				EventManager.getInstance().dispatchEvent(new CaseEvent<ICase>(entity, CaseEventType.CASE_OPENED));
				entity.getSource().addToken(new IToken() {

					@Override
					public Object getData() {
						// TODO: Should we obtain requisite Token from the
						// workflow Context?
						// TODO: Replace Token with actual Token.
						return "Token 1";
					}
				});
			} else
				throw new WorkflowException(String.format(
						"Case %s could not be started, it is not in an enabled state.", entity.getLabel()));
		}
	}

	/**
	 * {@inheritDoc}
	 * 
	 * @see com.google.code.netz.wfe.api.ILifecycleHandler#stop(com.google.code.netz.wfe.api.IManaged,
	 *      java.lang.Object)
	 */
	@Override
	public void stop(ICase<?> entity, Object workflowContext) throws WorkflowException {
		synchronized (mutex) {
			// TODO: Case status should be updated in a synchronized block
			// TODO: Evaluated current state of the Case,
			// if the Case completed without error then the state should be set
			// to
			// COMPLETED else should be set to ERROR
			if (entity.getCurrentState() == State.STATE_RUNNING) {
				entity.setCurrentState(State.STATE_COMPLETED);
				EventManager.getInstance().dispatchEvent(new CaseEvent<ICase>(entity, CaseEventType.CASE_CLOSED));
			} else
				throw new WorkflowException(String.format("Case %s could not be stopped, it is not currently running.",
						entity.getLabel()));
		}
	}

	/**
	 * {@inheritDoc}
	 * 
	 * @see com.google.code.netz.wfe.api.ILifecycleHandler#suspend(com.google.code.netz.wfe.api.IManaged,
	 *      java.lang.Object)
	 */
	@Override
	public void suspend(ICase<?> entity, Object workflowContext) throws WorkflowException {
		synchronized (mutex) {
			// TODO: Case status should be updated in a synchronized block
			if (entity.getCurrentState() == State.STATE_RUNNING) {
				entity.setCurrentState(State.STATE_SUSPENDED);
				EventManager.getInstance().dispatchEvent(new CaseEvent<ICase>(entity, CaseEventType.CASE_SUSPENDED));
			} else
				throw new WorkflowException(String.format(
						"Case %s could not be suspended, it is not currently running.", entity.getLabel()));
		}
	}

}
